using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Zune.Forms
{
    /// <summary>
    /// A TextBox Control.
    /// </summary>
    public class TextBox : Control
    {
        #region Public Events
        /// <summary>
        /// Fires when the text changes.
        /// </summary>
        public event EventHandler TextChanged;
        #endregion

        #region Private/Internal Members
        private Texture2D _vLine;
        private Texture2D _hLine;
        private Texture2D _cursor;
        private SpriteFont _font;
        //private Color _borderColor = Color.Black;
        private string _text = string.Empty;
        //private System.Threading.Timer _cursorTimer;
        private bool _isActive = false;
        private bool _displayCursor = false;
        private Control _parent;
        private Vector2 _cursorLocation = new Vector2(1, 5);
        private ContextMenu _contextMenu;
        private MenuItem _miCopy;
        private MenuItem _miPaste;
        private Enumerations.TextModes _mode = Enumerations.TextModes.SingleLine;
        private Vector2 _startPos = new Vector2(0, 0);
        private Vector2 _currPos = new Vector2(0, 0);
        private Vector2 _endPos = new Vector2(0, 0);
        private float _y = 0;
        private float _prevY = 0;
        private bool _isTouching = false;
        #endregion

        #region Constructors
        /// <summary>
        /// A TextBox.
        /// </summary>
        /// <param name="form">The Form that owns the TextBox.</param>
        public TextBox(Form form) : base(form)
        {
            base.Location = new Vector2(10, 10);
            base.Size = new Vector2(150, 40);
            base.BackColor = Color.White;
            base.ForeColor = Color.Black;
            _contextMenu = new ContextMenu(this);
            _miCopy = new MenuItem(_contextMenu);
            _miCopy.Text = "Copy";
            _miCopy.Enabled = false;
            _miCopy.Clicked += new EventHandler(_miCopy_Clicked);
            _miPaste = new MenuItem(_contextMenu);
            _miPaste.Text = "Paste";
            _miPaste.Clicked += new EventHandler(_miPaste_Clicked);
            _miPaste.Enabled = _form.ClipboardContainsText();
            _form.ClipboardTextChanged += new Clipboard.OnClipboardTextChanged(_form_ClipboardTextChanged);
            _borderType = Enumerations.BorderType.Border3D;
            //_cursorTimer = new System.Threading.Timer(_cursorTimer_Tick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }

        internal TextBox(Control parent)
            : base(parent)
        {
            _displayCursor = true;
            _parent = parent;
            base.Location = new Vector2(10, 10);
            base.Size = new Vector2(150, 50);
            base.BackColor = Color.White;
            SetCursorLocationByText();
            _borderType = Enumerations.BorderType.BorderSingle;
            //_cursorTimer = new System.Threading.Timer(_cursorTimer_Tick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }
        #endregion

        #region Event Handlers
        protected void _miPaste_Clicked(object sender, EventArgs e)
        {
            Text = _form.GetClipboardText();
        }

        protected void _miCopy_Clicked(object sender, EventArgs e)
        {
            _form.SetClipboardText(GetText(false));
        }

        protected void _form_ClipboardTextChanged(object sender, bool hasText)
        {
            _miPaste.Enabled = hasText;
        }
        #endregion

        #region Overrides
        public override void LoadContent()
        {
            _startPos = base.Location;
            _currPos = base.Location;
            _font = _form.Content.Load<SpriteFont>("DefaultFont");
            base.LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            if (_visible)
            {
                Vector2 tmpSize = _font.MeasureString("TEST");
                _cursor = Drawing.CreateLine(GraphicsDevice, 0, 0, 1, (Int32)tmpSize.Y - (Int32)(tmpSize.Y / 10), Color.Black);

                base.Draw(gameTime);

                switch (_mode)
                {
                    case Enumerations.TextModes.Password:
                    case Enumerations.TextModes.SingleLine:
                        SpriteBatch.DrawString(_font, GetText(true), new Vector2(_location.X, _location.Y), _foreColor);
                        break;
                    case Enumerations.TextModes.Multiline:
                        List<string> lines = Utilities.GetLinesFromString(_font, _text, _location, _size);

                        SetCurrentY();
                        
                        Vector2 textLocation = new Vector2(_location.X, _location.Y + _y);
                        foreach (string line in lines)
                        {
                            //if (Utilities.IsLocationVisible(textLocation, _location, _size))
                            //{
                                base.SpriteBatch.DrawString(_font, line, textLocation, _foreColor);
                            //}
                            Vector2 textSize = _font.MeasureString(GetText(true));
                            textLocation = new Vector2(textLocation.X, textLocation.Y + textSize.Y);
                        }
                        break;
                }
                
                if (_displayCursor && !_isTouching)
                    SpriteBatch.Draw(_cursor, _cursorLocation, Color.White);
                
            }
        }

        private void SetCurrentY()
        {
            if (_mode == Enumerations.TextModes.Multiline)
            {
                List<string> lines = Lines;
                Vector2 textSize = _font.MeasureString(_text);
                if (_y > 0 && !_isTouching)
                {
                    if (_y < 15)
                    {
                        _y = 0;
                    }
                    else if (_y <= 30)
                    {
                        _y -= 15;
                    }
                    else if (_y > 30 && _y <= 100)
                    {
                        _y -= 20;
                    }
                    else if (_y > 100)
                    {
                        _y -= 40;
                    }
                    else
                    {
                        _y = 0;
                    }
                    _currPos = base.Location;
                    _startPos = base.Location;
                    _prevY = _y;
                }
                //Did we go past the last item?
                else if (_y < _size.Y - (lines.Count * textSize.Y) && !_isTouching)
                {
                    float _tempY = _size.Y - (lines.Count * textSize.Y) - _y;
                    if (_tempY < 15)
                    {
                        _y = _size.Y - (lines.Count * textSize.Y) - 20;
                    }
                    if (_tempY <= 50)
                    {
                        _y += 15;
                    }
                    else if (_tempY > 50 && _tempY <= 110)
                    {
                        _y += 20;
                    }
                    else if (_tempY > 110)
                    {
                        _y += 40;
                    }
                    else
                    {
                        _y = _size.Y - (lines.Count * textSize.Y) - 20;
                    }

                    _currPos = new Vector2(_currPos.X, _y);
                    _startPos = _currPos;
                    _prevY = _y;
                }
                else if (_isTouching)
                {
                    _y = _currPos.Y - _startPos.Y + _prevY;
                }
            }           
        }

        public override void Update(GameTime gameTime, TouchCollection touches)
        {
            if (base._visible)
            {
                if (touches.Count > 0)
                {
                    TouchLocation touch = touches[0];
                    if (Utilities.IsControlTouched(touch.Position, _location, _size))
                    {
                        _isTouching = true;

                        switch (touch.State)
                        {
                            case TouchLocationState.Pressed:
                                _startPos = touch.Position;
                                _currPos = touch.Position;
                                _endPos = touch.Position;
                                break;
                            case TouchLocationState.Moved:
                                _currPos = touch.Position;
                                break;
                            case TouchLocationState.Released:
                                _startPos = base.Location;
                                _endPos = touch.Position;
                                _currPos = base.Location;
                                _prevY = _y;
                                _isTouching = false;
                                if (Utilities.IsControlClicked(touch, _location, _size))
                                {
                                    if (_contextMenu != null)
                                    {
                                        if (_contextMenu._visible)
                                            return;
                                    }

                                    if (!_form.IsKeyboardDisplayed)
                                    {
                                        _form.ShowKeyboard(this);
                                    }

                                    //if (_parent != null)
                                    //    _cursorTimer.Change(500, 500);

                                    SetCursorLocationByTouch(touch.Position);
                                }
                                break;
                            
                        }
                    }
                    else
                    {
                        _isTouching = false;
                    }
                }
                else
                {
                    _isTouching = false;
                }

                base.Update(gameTime, touches);
            }
        }
        #endregion

        #region Private/Internal Methods
        private string GetText(bool showLastLetter)
        {
            string passText = string.Empty;
            if (_mode == Enumerations.TextModes.Password)
            {
                char[] chrs = _text.ToCharArray();
                for (int i = 0; i < chrs.Length; i++)
                {
                    if (showLastLetter && i == chrs.Length - 1)
                        passText += chrs[i];
                    else
                        passText += "*";
                }
            }
            else
                passText = _text;
            return passText;
        }

        //internal void DeterminScrollYByLineHeight()
        //{
        //    _y = _currPos.Y - _startPos.Y + _prevY;

        //    List<string> lines = Lines;
        //    Vector2 tempSize = _font.MeasureString(_text);
        //    for (int i = 0; i < lines.Count; i++)
        //    {
        //        float tempY = _location.Y + (i * tempSize.Y);
        //        float tempHalfWay = tempY + (tempSize.Y / 2);
        //        float tempSizeY = tempY + tempSize.Y;

        //        if (_y >= tempY && _y <= tempHalfWay)
        //        {
        //            _location = new Vector2(_location.X, tempY);
        //        }
        //        else if (_y > tempHalfWay && _y <= tempSizeY)
        //        {
        //            _location = new Vector2(_location.X, tempSizeY);
        //        }
        //    }

        //}

        internal void SetCursorLocationByText()
        {
            List<string> lines = Lines;
            if (!string.IsNullOrEmpty(_text) && lines.Count == 1)
            {
                Vector2 textSize = _font.MeasureString(GetText(true));
                _cursorLocation = new Vector2(_location.X + textSize.X + 1, _location.Y + (textSize.Y / 10));
            }
            else if (!string.IsNullOrEmpty(_text) && lines.Count > 1)
            {
                Vector2 textSize = _font.MeasureString(lines[lines.Count-1]);
                _cursorLocation = new Vector2(_location.X + textSize.X + 1, _location.Y + (textSize.Y * lines.Count) + (textSize.Y / 10));
            }
            else
            {
                _cursorLocation = new Vector2(_location.X + 1, _location.Y + 5);
            }
        }

        private void SetCursorLocationByTouch(Vector2 touchPos)
        {
            if (_mode == Enumerations.TextModes.Password)
            {
                char[] chars = GetText(true).ToCharArray();
                string _tmpText = string.Empty;
                foreach (char chr in chars)
                {
                    _tmpText += chr.ToString();
                    Vector2 textSize = _font.MeasureString(_tmpText);
                    if (touchPos.X < textSize.X)
                    {
                        _cursorLocation = new Vector2(textSize.X + 1, _location.Y + _y + (textSize.Y / 10));
                        return;
                    }
                }
            }
            else
            {
                string currentLine = Lines[GetCurrentLineNumber(touchPos)];
                Vector2 tmpTextSize = _font.MeasureString(currentLine);
                Vector2 currentLinePos = GetCurrentLineStartPositionByTouch(touchPos);
                char[] chars = currentLine.ToCharArray();
                string _tmpText = string.Empty;
                foreach (char chr in chars)
                {
                    _tmpText += chr.ToString();
                    Vector2 textSize = _font.MeasureString(_tmpText);
                    if (touchPos.X < textSize.X)
                    {
                        _cursorLocation = new Vector2(textSize.X + 1, currentLinePos.Y + (tmpTextSize.Y / 10));
                        return;
                    }
                }
            }
            SetCursorLocationByText();
        }

        internal void KeyboardClicked(string text)
        {
            Vector2 textSize = _font.MeasureString(GetText(true));
            if (_cursorLocation.X > textSize.X)
            {
                Text = _text += text;
                Vector2 newTextSize = _font.MeasureString(GetText(true));
                _cursorLocation = new Vector2(_location.X + newTextSize.X + 1, _location.Y + (textSize.Y / 10));
            }
            else
            {
                string _tmpText = string.Empty;
                string _tmpFullTextPrev = string.Empty;
                string _tmpTextPrev = string.Empty;
                bool textInserted = false;
                List<string> lines = Lines;
                Vector2 fullTextSize = _font.MeasureString(_text);
                for (int i = 0; i < lines.Count; i++)
                {
                    if (_cursorLocation.Y > (i * fullTextSize.Y) + _y + _location.Y && _cursorLocation.Y < (i * fullTextSize.Y) + fullTextSize.Y + _y + _location.Y)
                    {
                        char[] chars = lines[i].ToCharArray();
                        foreach (char chr in chars)
                        {
                            _tmpText += chr.ToString();
                            Vector2 tmpTextSize = _font.MeasureString(_tmpText);
                            Vector2 tmpTextPrevSize = _font.MeasureString(_tmpTextPrev);
                            if (_cursorLocation.X > tmpTextPrevSize.X && _cursorLocation.X < tmpTextSize.X && !textInserted)
                            {
                                _tmpTextPrev += text;
                                Vector2 newTextSize = _font.MeasureString(_tmpTextPrev);
                                _cursorLocation = new Vector2(_location.X + newTextSize.X + 1, _location.Y + _y + (textSize.Y * i) + (textSize.Y / 10));
                                textInserted = true;
                                _tmpText = _tmpTextPrev += chr.ToString();
                            }
                            _tmpTextPrev = _tmpText;
                        }
                        _tmpFullTextPrev += _tmpTextPrev;
                    }
                    else
                    {
                        _tmpFullTextPrev += lines[i];
                    }
                }
                Text = _tmpFullTextPrev;
            }
        }

        internal void BackspaceClicked()
        {
            Vector2 textSize = _font.MeasureString(GetText(true));
            if (_cursorLocation.X > textSize.X && _location.Y == 0)
            {
                Text = _text.Substring(0, _text.Length - 1);
                Vector2 newTextSize = _font.MeasureString(GetText(true));
                _cursorLocation = new Vector2(_location.X + newTextSize.X + 1, _location.Y + (textSize.Y / 10));
            }
            else
            {
                string _tmpText = string.Empty;
                string _tmpFullTextPrev = string.Empty;
                string _tmpTextPrev = string.Empty;
                bool textRemoved = false;
                List<string> lines = Lines;
                Vector2 fullTextSize = _font.MeasureString(_text);
                for (int i = 0; i < lines.Count; i++)
                {
                    if (_cursorLocation.Y > (i * fullTextSize.Y) + _y + _location.Y && _cursorLocation.Y < (i * fullTextSize.Y) + fullTextSize.Y + _y + _location.Y)
                    {
                        char[] chars = lines[i].ToCharArray();
                        foreach (char chr in chars)
                        {
                            _tmpText += chr.ToString();
                            Vector2 tmpTextSize = _font.MeasureString(_tmpText);
                            Vector2 tmpTextPrevSize = _font.MeasureString(_tmpTextPrev);
                            if (_cursorLocation.X > tmpTextPrevSize.X && _cursorLocation.X < tmpTextSize.X && !textRemoved)
                            {
                                _tmpTextPrev = _tmpTextPrev.Substring(0, _tmpTextPrev.Length - 1);
                                Vector2 newTextSize = _font.MeasureString(_tmpTextPrev);
                                _cursorLocation = new Vector2(_location.X + newTextSize.X + 1, _location.Y + _y + (textSize.Y * i) + (textSize.Y / 10));
                                textRemoved = true;
                                _tmpText = _tmpTextPrev += chr.ToString();
                            }
                            _tmpTextPrev = _tmpText;
                        }
                        _tmpFullTextPrev += _tmpTextPrev;
                    }
                    else
                    {
                        _tmpFullTextPrev += lines[i];
                    }
                }
                Text = _tmpFullTextPrev;
            }
        }

        internal bool IsCursorVisible()
        {
            Vector2 tmpSize = _font.MeasureString(Lines[GetCurrentLineFromCurrentCursorPosition()]);
            if (_cursorLocation.X + 1 < _location.X + _size.X - 1 && _cursorLocation.Y + (Int32)tmpSize.Y - (Int32)(tmpSize.Y / 10) < _location.Y + _size.X)
            {
                return true;
            }

            return false;
        }

        internal int GetCurrentLineFromCurrentCursorPosition()
        {
            return GetCurrentLineNumber(_cursorLocation);
        }

        internal Vector2 GetCurrentLineStartPositionByTouch(Vector2 touchPos)
        {
            return GetCurrentLineStartLocation(GetCurrentLineNumber(touchPos));
        }

        internal int GetCurrentLineNumber(Vector2 touchPos)
        {
            if (_mode == Enumerations.TextModes.Multiline)
            {
                List<string> lines = Lines;
                for (int i = 0; i < lines.Count; i++)
                {
                    Vector2 tmpTextSize = _font.MeasureString(lines[i]);
                    float startY = _location.Y + i * tmpTextSize.Y + _y;
                    float endY = _location.Y + (i + 1) * tmpTextSize.Y + _y;
                    if (touchPos.Y > startY && touchPos.Y < endY)
                    {
                        return i;
                    }
                }
            }

            return 0;
        }

        internal Vector2 GetCurrentLineStartLocation(int index)
        {
            Vector2 ret = _location;
            List<string> lines = Lines;
            Vector2 fullStringSize = _font.MeasureString(_text);

            if (lines.Count > index)
            {
                Vector2 lineSize = _font.MeasureString(lines[index]);
                ret = new Vector2(_location.X, _location.Y + _y + (index * fullStringSize.Y));
            }
            
            return ret;
        }
        #endregion

        //protected void _cursorTimer_Tick(object obj)
        //{
        //    _displayCursor = !_displayCursor;
        //}

        #region Public Properties
        /// <summary>
        /// 
        /// </summary>
        public List<string> Lines
        {
            get 
            {
                if (_mode == Enumerations.TextModes.Multiline)
                    return Utilities.GetLinesFromString(_font, GetText(false), _location, _size);
                else
                {
                    List<string> ret = new List<string>();
                    ret.Add(GetText(false));
                    return ret;
                }
            }
        }

        /// <summary>
        /// Gets or sets the text of the TextBox.
        /// </summary>
        public string Text
        {
            get { return GetText(false); }
            set 
            {
                _text = value;
                if (_miCopy != null)
                    _miCopy.Enabled = !string.IsNullOrEmpty(_text);
                
                if (TextChanged != null)
                {
                    TextChanged(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Gets or sets the mode of the TextBox.  Default is SingleLine.
        /// </summary>
        public Enumerations.TextModes TextMode
        {
            get { return _mode; }
            set { _mode = value; }
        }
        #endregion
    }
}